import type {
  LanguageClientOptions,
  ServerOptions,
} from "vscode-languageclient/node.js";
import type * as vscode from "vscode";
import * as path from "node:path";
import { LanguageClient, TransportKind } from "vscode-languageclient/node.js";
import {
  MonacoEditorLanguageClientWrapper,
  UserConfig,
} from "monaco-editor-wrapper/bundle";
import { Uri } from "vscode";

let client: LanguageClient;

// This function is called when the extension is activated.
export function activate(context: vscode.ExtensionContext): void {
  client = startLanguageClient(context);
  initMonacoEditor(context);
}

// This function is called when the extension is deactivated.
export function deactivate(): Thenable<void> | undefined {
  if (client) {
    return client.stop();
  }
  return undefined;
}

function startLanguageClient(context: vscode.ExtensionContext): LanguageClient {
  const serverModule = context.asAbsolutePath(
    path.join("out", "language", "main.cjs")
  );
  // The debug options for the server
  // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging.
  // By setting `process.env.DEBUG_BREAK` to a truthy value, the language server will wait until a debugger is attached.
  const debugOptions = {
    execArgv: [
      "--nolazy",
      `--inspect${process.env.DEBUG_BREAK ? "-brk" : ""}=${
        process.env.DEBUG_SOCKET || "6009"
      }`,
    ],
  };

  // If the extension is launched in debug mode then the debug server options are used
  // Otherwise the run options are used
  const serverOptions: ServerOptions = {
    run: { module: serverModule, transport: TransportKind.ipc },
    debug: {
      module: serverModule,
      transport: TransportKind.ipc,
      options: debugOptions,
    },
  };

  // Options to control the language client
  const clientOptions: LanguageClientOptions = {
    documentSelector: [{ scheme: "*", language: "sys-y-plus" }],
  };

  // Create the language client and start the client.
  const client = new LanguageClient(
    "sys-y-plus",
    "SysYPlus",
    serverOptions,
    clientOptions
  );

  // Start the client. This will also launch the server
  client.start();
  return client;
}

// Monaco Editor Initialization
async function initMonacoEditor(context: vscode.ExtensionContext) {
  const editorElement = document.createElement("div");
  editorElement.id = "monaco-editor-root";
  document.body.appendChild(editorElement);

  const wrapper = new MonacoEditorLanguageClientWrapper();

  // 加载语法文件
  const grammarPath = Uri.joinPath(context.extensionUri, 'syntaxes', 'sys-y-plus.tmLanguage.json').fsPath;
  const languageDef = await loadGrammar(grammarPath);

  const userConfig: UserConfig = {
    htmlElement: editorElement,
    wrapperConfig: {
      editorAppConfig: {
        $type: "classic",
        languageId: "sys-y-plus",
        useDiffEditor: false,
        code: "void main() { int a; a = 0; }",
        theme: "vs-dark",
        languageDef: languageDef,
      },
      serviceConfig: {
        enableModelService: true,
        configureConfigurationService: {
          defaultWorkspaceUri: "/tmp/",
        },
        enableKeybindingsService: true,
        enableLanguagesService: true,
        debugLogging: false,
      },
    },
    languageClientConfig: {
      options: {
        $type: "WorkerDirect",
        worker: new Worker(
          Uri.joinPath(
            context.extensionUri,
            "SysYPlus-server-worker.js"
          ).fsPath,
          {
            type: "module",
            name: "sysyplusLS",
          }
        ),
        name: "sys-y-plus-language-server-worker",
      },
    },
  };

  // 确保 setup 函数在 wrapper.start 调用之前执行
  // setup();
  await wrapper.start(userConfig);
}

// 加载语法定义文件的辅助函数
async function loadGrammar(path: string): Promise<any> {
  try {
    const response = await fetch(path);
    if (!response.ok) {
      throw new Error(`无法加载语法文件: ${response.statusText}`);
    }
    return await response.json();
  } catch (error: unknown) {
    console.error(`加载语法文件失败: ${(error as Error).message}`);
    return getMonarchGrammar(); // 回退到默认语法定义
  }
}

function getMonarchGrammar() {
  return {
    keywords: [
      "break",
      "const",
      "continue",
      "else",
      "float",
      "if",
      "int",
      "person",
      "return",
      "void",
      "while",
    ],
    operators: ["!", "!="],
    symbols:
      /!|!=|%|&&|\(|\)|\*|\+|,|-|\/|;|<|<=|=|==|>|>=|\[|\]|\{|$$|\||\|\}/,

    tokenizer: {
      initial: [
        { regex: /[0-9]+/, action: { token: "number" } },
        { regex: /[0-9]+\.[0-9]*/, action: { token: "number" } },
        { regex: /\.[0-9]*/, action: { token: "number" } },
        {
          regex: /int|float/,
          action: {
            cases: {
              "@keywords": { token: "keyword" },
              "@default": { token: "BType" },
            },
          },
        },
        {
          regex: /void|int|float/,
          action: {
            cases: {
              "@keywords": { token: "keyword" },
              "@default": { token: "FuncType" },
            },
          },
        },
        {
          regex: /[a-zA-Z0-9]+/,
          action: {
            cases: {
              "@keywords": { token: "keyword" },
              "@default": { token: "Ident" },
            },
          },
        },
        { include: "@whitespace" },
        {
          regex: /@symbols/,
          action: {
            cases: {
              "@operators": { token: "operator" },
              "@default": { token: "" },
            },
          },
        },
      ],
      whitespace: [
        { regex: /\s+/, action: { token: "white" } },
        { regex: /\/\*/, action: { token: "comment", next: "@comment" } },
        { regex: /\/\/[^\n\r]*/, action: { token: "comment" } },
      ],
      comment: [
        { regex: /[^/\*]+/, action: { token: "comment" } },
        { regex: /\*\//, action: { token: "comment", next: "@pop" } },
        { regex: /[/\*]/, action: { token: "comment" } },
      ],
    },
  };
}
